from hashlib import md5
import os

from utils.misc import callable_name

from stacking.instance.benchmarks.auxiliary import *
from stacking.instance.gen.basic import generate_items
from stacking.instance import Instance


def generate_benchmark(param_list, base_dir=".", skip_found=True):
    """Generates a benchmark using the basic item generator."""
    # parameter validation (check if all required parameters were provided)
    required_params = set(["nitems", "nstacks", "item_stay_distr", 
                           "due_interval_distr", "order_size", "version"])
    provided_params = set(param for param, _ in param_list)
    if not provided_params >= required_params:
        raise Exception("missing parameters - %s" % (required_params - provided_params))
    
    log("Generating benchmark with parameter list:\n")
    ninstances = 1
    for param, values in param_list:
        log("%20s = %s\n" % (param, values))
        ninstances *= len(values)
    log("Total %d instances.\n" % ninstances)
    
    _, base_dir  = affirm_dir(os.path.abspath(base_dir))
    for params in param_combinations(param_list):
        set_dir_and_filename(base_dir, params)
        if skip_found and os.path.isfile(params.filepath):
            log(" ... skipping %s\n" % params.filename)
            continue
            
        log(" ... generating %s ... " % params.filename)
        items = generate_items(seed=compute_seed(params), **params)
        log("done. Saving... ")
        instance = Instance.quick_build(items, params.nstacks)
        instance.meta.update((k, callable_name(v) if callable(v) else v) 
                             for k, v in params.iteritems())
        instance.save()
        log("done.\n")
        

def set_dir_and_filename(base_dir, params):
    new, params.directory = affirm_dir(base_dir, 
                                       "I%d" % params.nitems,
                                       "S%d" % params.nstacks, 
                                       "O%d" % params.order_size,
                                       "ItemStay_%s" % params.item_stay_distr.__name__,
                                       "DueInterval_%s" % params.due_interval_distr.__name__)
    params.filename  = "instance%02d.sp" % params.version
    params.filepath  = os.path.join(params.directory, params.filename)
    if new:
        log("." * 100)
        log("\nCreated directory %s\n" % params.directory)
        log("Instance group parameters:\n")
        for k, v in params.iteritems():
            log("%20s = %s\n" % (k, v))
        save_params_file(params)
        
        
def compute_seed(params):
    params_str = ":".join("%s=%s" % (k, v.__name__ if callable(v) else v) 
                          for k, v in sorted(params.iteritems())) 
    return int(md5(params_str).hexdigest(), 16)
    
    
def save_params_file(params):
    filepath = os.path.join(params.directory, "parameters.txt")
    log("Saving parameters into %s... " % filepath)
    with open(filepath, "w") as ostream:
        ostream.write("# Instance group parameters\n")
        for k, v in sorted(params.iteritems()):
            if callable(v):
                v = callable_name(v)
            ostream.write("%s = %s\n" % (k, v))
    log("done.\n")
    
    
param_list = [("nitems", [100, 1000, 10000]),
              ("nstacks", [5, 10, 20, 50]),
              ("item_stay_distr", [sample("uniform", 1.0, 100.0), sample("gauss", 50.0, 10.0)]),
              ("due_interval_distr", [sample("uniform", 1.0, 100.0), sample("gauss", 50.0, 10.0)]),
              ("order_size", [1, 2, 5, 10]),
              ("version", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])]

